{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from torch import nn,optim\n",
    "from torch.autograd import Variable\n",
    "from torchvision import datasets, transforms\n",
    "from torch.utils.data import DataLoader\n",
    "import numpy as np\n",
    "import torch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 读取数据\n",
    "# 训练集\n",
    "train_dataset = datasets.MNIST(root='./',\n",
    "                             train=True,\n",
    "                             transform=transforms.ToTensor(),\n",
    "                             download=True)\n",
    "# 测试集\n",
    "test_dataset = datasets.MNIST(root='./',\n",
    "                             train=True,\n",
    "                             transform=transforms.ToTensor(),\n",
    "                             download=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 批次大小\n",
    "batch_size = 64\n",
    "\n",
    "# 装载训练集，其中shuffle表示打乱顺序，将data每64一份分成若干份进行装载\n",
    "train_loader = DataLoader(dataset=train_dataset,\n",
    "                         batch_size=batch_size,\n",
    "                         shuffle=True)\n",
    "\n",
    "# 装载测试集，其中shuffle表示打乱顺序\n",
    "test_loader = DataLoader(dataset=test_dataset,\n",
    "                         batch_size=batch_size,\n",
    "                         shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([64, 1, 28, 28])\n",
      "torch.Size([64])\n"
     ]
    }
   ],
   "source": [
    "# enumerate的作用是将一个可遍历的数据对象（例如列表、元组或字符串）组合为一个索引序列\n",
    "for i, data in enumerate(train_loader):\n",
    "    inputs, labels = data\n",
    "    print(inputs.shape)# inputs是64*1*28*28，表示64个图片，每个图片一个通道，28*28像素\n",
    "    print(labels.shape)# labels是标志每个图片的数字是几\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0, 8, 3, 8, 3, 0, 4, 4, 0, 8, 5, 2, 8, 6, 1, 7, 8, 7, 9, 0, 6, 8, 5, 1,\n",
       "        0, 9, 6, 4, 4, 6, 8, 0, 9, 3, 2, 2, 3, 6, 6, 2, 6, 0, 1, 3, 4, 5, 5, 9,\n",
       "        7, 5, 8, 4, 1, 5, 0, 5, 2, 0, 6, 3, 6, 0, 5, 0])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义网络结构\n",
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.fc1 = nn.Linear(784, 10)# 784个输入——28*28,10个输出——标签\n",
    "        self.softmax = nn.Softmax(dim=1)# 指定激活函数，所作用的维度为1（维度从0开始，本例也就是作用于输出的这一维度） \n",
    "    def forward(self, x):\n",
    "        # ([64, 1, 28, 28])->(64, 784)将inputs的维度转换为两维，因为神经网络中间层需要做矩阵运算必须是两维\n",
    "        x = x.view(x.size()[0], -1)# view作用类似reshape，size()作用是获取每个维度的大小\n",
    "        x = self.fc1(x)\n",
    "        x = self.softmax(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义模型\n",
    "model = Net()\n",
    "# 定义损失函数\n",
    "mse_loss = nn.MSELoss()\n",
    "# 定义优化器\n",
    "optimizer = optim.SGD(model.parameters(), 0.5)# 学习率是0.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train():\n",
    "    for i, data in enumerate(train_loader):\n",
    "        # 获得一个批次的数据和标签\n",
    "        inputs, labels = data\n",
    "        # 获得模型的预测结果\n",
    "        out = model(inputs)\n",
    "        # （64）->（64, 1）\n",
    "        labels = labels.reshape(-1, 1)\n",
    "        # 把数据标签变成独热编码，也就是一个数组只有一个下标是“1”，其余都是“0”\n",
    "        # torch.zeros(inputs.shape[0], 10)生成一个维度符合参数要求的零矩阵\n",
    "        # scatter(1, labels, 1)指将数组第“lables”个位置的元素置为1\n",
    "        one_hot = torch.zeros(inputs.shape[0], 10).scatter(1, labels, 1)\n",
    "        # 计算损失\n",
    "        loss = mse_loss(out, one_hot)\n",
    "        # 梯度清零\n",
    "        optimizer.zero_grad()\n",
    "        # 计算梯度\n",
    "        loss.backward()\n",
    "        # 修改权值\n",
    "        optimizer.step()\n",
    "    \n",
    "def test():\n",
    "    correct = 0\n",
    "    for i, data in enumerate(test_loader):\n",
    "        # 获得一个批次的数据和标签\n",
    "        inputs, labels = data\n",
    "        # 获得模型预测结果（64,10）\n",
    "        out = model(inputs)\n",
    "        # 获得最大值，以及最大值所在位置\n",
    "        _,predicted = torch.max(out, 1)\n",
    "        # \n",
    "        correct += (predicted == labels).sum()\n",
    "    print(\"Test acc:{0}\".format(correct.item()/len(test_dataset)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 0\n",
      "Test acc:0.8942666666666667\n",
      "epoch: 1\n",
      "Test acc:0.90225\n",
      "epoch: 2\n",
      "Test acc:0.9064166666666666\n",
      "epoch: 3\n",
      "Test acc:0.9091333333333333\n",
      "epoch: 4\n",
      "Test acc:0.91195\n",
      "epoch: 5\n",
      "Test acc:0.9137\n",
      "epoch: 6\n",
      "Test acc:0.9158666666666667\n",
      "epoch: 7\n",
      "Test acc:0.9171166666666667\n",
      "epoch: 8\n",
      "Test acc:0.9175166666666666\n",
      "epoch: 9\n",
      "Test acc:0.9190833333333334\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(10):\n",
    "    print('epoch:',epoch)\n",
    "    train()\n",
    "    test()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorchStudy",
   "language": "python",
   "name": "pytorchstudy"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
